// 定义Snake类
export default class Snake {
    // 蛇头
    head: HTMLElement;
    // 获取蛇的容器
    private _element: HTMLElement;
    // 蛇身
    bodies: HTMLCollection; // html元素的集合类型，当我们往蛇身体里面添加新元素后，也会自动获取新的元素

    constructor() {
        this._element = document.querySelector('#snake')!;
        this.head = document.querySelector('#snake>div')!;
        this.bodies = this._element.getElementsByTagName('div');
    }

    get element(): HTMLElement {
        return this._element;
    }

    set element(value: HTMLElement) {
        this._element = value;
    }
    // 获取蛇的坐标（蛇头坐标） X轴
    get X() {
        return this.head.offsetLeft;
    }
    get Y() {
        return this.head.offsetTop;
    }

    // 设置蛇头的X/Y坐标
    // 设置蛇的坐标（蛇头坐标） X轴
    set X(value: number) {
        // 蛇的新位置与旧位置相同时，不进行操作，直接return
        if (this.X === value) {
            return;
        } else if (value < 0 || value > 290) {  // 判断是否撞墙
            // 撞墙了，蛇死，throw抛出错误
            throw new Error('蛇撞墙了！')
        }

        // 禁止掉头（第一节与第二节身体坐标重复证明掉头）
        // 修改X时，是修改水平坐标，蛇向左移动时 不能向右掉头（只有一节头的时候可以）
        if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {  // 判断第二节存在，且第二节坐标与蛇头重合
            // console.log('水平掉头了');
            // 如果发生掉头，反方向移动，即使其按原来方向移动
            // 如果掉头时新值value > 旧值this.X ，证明向右掉头，value = this.X -10使其继续向左,
            if (value > this.X) {
                value = this.X - 10;
            } else {
                // 新值value < 旧值this.X，证明掉头向左，value = this.X + 10 使其继续向右
                value = this.X + 10;
            }
        }

        // 调用身体移动的方法
        this.moveBody();

        this.head.style.left = value + "px";

        // 检查蛇是否撞到自己身体
        this.checkHead();
    }

    set Y(value: number) {
        // 新值和旧值一样，不需要修改
        if (this.Y === value) {
            return;
        } else if (value < 0 || value > 290) {
            // 撞墙了，蛇死，throw抛出错误
            throw new Error('蛇撞墙了！')
        }

        // 禁止掉头（第一节与第二节身体坐标重复证明掉头）
        // 修改Y时，是修改垂直坐标，蛇向上移动时 不能向下掉头（只有一节头的时候可以）
        if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {  // 判断第二节存在，且第二节坐标与蛇头重合
            // console.log('垂直掉头了');
            // 如果发生掉头，反方向移动，即使其按原来方向移动
            // 如果掉头时新值value > 旧值this.Y ，证明向下掉头，value = this.Y -10使其继续向上,
            if (value > this.Y) {
                value = this.Y - 10;
            } else {
                // 新值value < 旧值this.X，证明掉头向左，value = this.Y + 10 使其继续向下
                value = this.Y + 10;
            }
        }


        // 调用身体移动的方法
        this.moveBody();

        this.head.style.top = value + "px";

        // 检查蛇是否撞到自己身体
        this.checkHead();
    }

    // 蛇身体增加的方法
    addBody() {
        // 向element中添加一个div
        // insertAdjacentHTML() 向子节点中添加HTML元素，第一个参数为添加位置：beforeend 父标签内部末尾，第二个参数为HTML元素
        this.element.insertAdjacentHTML("beforeend", "<div></div>");
    }

    // 蛇身体移动的方法
    moveBody() {
        /*
        将后面的身体设置为前边身体的位置
            例：
                第三节 => 原来第二节位置
                第二节 => 原来第一节位置
                第一节 => 蛇头的新位置
     */
        // 遍历所有身体
        for (let i = this.bodies.length - 1; i > 0; i--) {
            // 设置前边身体的位置
            let X = (this.bodies[i - 1] as HTMLElement).offsetLeft;
            let Y = (this.bodies[i - 1] as HTMLElement).offsetTop;

            // 设置当前身体
            (this.bodies[i] as HTMLElement).style.left = X + "px";
            (this.bodies[i] as HTMLElement).style.top = Y + "px";
        }
    }

    // 判断头是否触碰身体
    checkHead() {
        // 获取所有身体，检查蛇头坐标是否与其重复
        for (let i = 1; i < this.bodies.length; i++) {
            let snakeData = this.bodies[i] as HTMLElement;
            if (this.X === snakeData.offsetLeft && this.Y === snakeData.offsetTop) {
                // 进入判断证明蛇头撞到身体，蛇死，throw抛出错误
                throw new Error('撞到自己身体！')
            }

        }
    }


}